/*
 * This code is part of ctrulib (https://github.com/smealum/ctrulib)
 */
#define  __SYLIXOS_KERNEL
#include <SylixOS.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "linux/types.h"
#include "fmt.h"
#include "console.h"
#include "con_sio.h"
#include "gfx.h"

//#include "font_6x10.h"
#include "font_8x16.h"

static int needCopy = 1;

static PrintConsole topCon;
static PrintConsole botCon;

//set up the palette for color printing
static u32 colorTable[] = {
    ARGB8888(  0,  0,  0),	// faint black
    ARGB8888(255,  0,  0),	// bright red
    ARGB8888(  0,255,  0),	// bright green
    ARGB8888(255,255,  0),	// bright yellow
    ARGB8888(  0,  0,255),	// bright blue
    ARGB8888(255,  0,255),	// bright magenta
    ARGB8888(  0,255,255),	// bright cyan
    ARGB8888(255,255,255),	// bright white

    ARGB8888( 64, 64, 64),	// almost black
    ARGB8888(224,  0,  0),	// accent red
    ARGB8888( 64,255, 64),	// accent green
    ARGB8888(255,255, 32),	// accent yellow
    ARGB8888( 64, 64,255),	// accent blue
    ARGB8888(255,  0,255),	// bright magenta
    ARGB8888(  0,255,255),	// bright cyan
    ARGB8888(192,192,192),	// almost white

    ARGB8888(128,128,128),	// bright black
    ARGB8888( 64,  0,  0),	// faint red
    ARGB8888(  0, 64,  0),	// faint green
    ARGB8888( 64, 64,  0),	// faint yellow
    ARGB8888(  0,  0, 64),	// faint blue
    ARGB8888( 64,  0, 64),	// faint magenta
    ARGB8888(  0, 64, 64),	// faint cyan
    ARGB8888( 96, 96, 96),	// faint white
};

static u16 colorTable16[] = {
    RGB8_to_565(  0,  0,  0),  // faint black
    RGB8_to_565(255,  0,  0),  // bright red
    RGB8_to_565(  0,255,  0),  // bright green
    RGB8_to_565(255,255,  0),  // bright yellow
    RGB8_to_565(  0,  0,255),  // bright blue
    RGB8_to_565(255,  0,255),  // bright magenta
    RGB8_to_565(  0,255,255),  // bright cyan
    RGB8_to_565(255,255,255),  // bright white

    RGB8_to_565( 64, 64, 64),  // almost black
    RGB8_to_565(224,  0,  0),  // accent red
    RGB8_to_565( 64,255, 64),  // accent green
    RGB8_to_565(255,255, 32),  // accent yellow
    RGB8_to_565( 64, 64,255),  // accent blue
    RGB8_to_565(255,  0,255),  // bright magenta
    RGB8_to_565(  0,255,255),  // bright cyan
    RGB8_to_565(192,192,192),  // almost white

    RGB8_to_565(128,128,128),  // bright black
    RGB8_to_565( 64,  0,  0),  // faint red
    RGB8_to_565(  0, 64,  0),  // faint green
    RGB8_to_565( 64, 64,  0),  // faint yellow
    RGB8_to_565(  0,  0, 64),  // faint blue
    RGB8_to_565( 64,  0, 64),  // faint magenta
    RGB8_to_565(  0, 64, 64),  // faint cyan
    RGB8_to_565( 96, 96, 96),  // faint white
};

PrintConsole defaultConsole =
{
	//Font:
	{
		default_font, //font gfx
		0, //first ascii character in the set
		256 //number of characters in the font set
	},
	(char*)NULL,(char*)NULL,
	0,0,0,
	0,0,	//cursorX cursorY
	0,0,	//prevcursorX prevcursorY
	SCREEN_WIDTH_DEFAULT / FONT_WIDTH,		//console width
	SCREEN_HEIGHT_DEFAULT / FONT_HEIGHT,		//console height
	0,		//window x
	0,		//window y
	SCREEN_WIDTH_DEFAULT / FONT_WIDTH,		//window width
	SCREEN_HEIGHT_DEFAULT / FONT_HEIGHT,		//window height
	4,		//tab size
	7,		// foreground color
	0,		// background color
	0,		// flags
	0,		//print callback
	FALSE	//console initialized
};

PrintConsole currentCopy;

PrintConsole* currentConsole = &currentCopy;

PrintConsole* consoleGetDefault(void){return &defaultConsole;}

void consolePrintChar(int c);
void consoleDrawChar(int c);

//---------------------------------------------------------------------------------
static void consoleCls(char mode) {
//---------------------------------------------------------------------------------

	int i = 0;
	int colTemp,rowTemp;

	switch (mode)
	{
	case '[':
	case '0':
		{
			colTemp = currentConsole->cursorX ;
			rowTemp = currentConsole->cursorY ;

			while(i++ < ((currentConsole->windowHeight * currentConsole->windowWidth) - (rowTemp * currentConsole->consoleWidth + colTemp)))
				consolePrintChar(' ');

			currentConsole->cursorX  = colTemp;
			currentConsole->cursorY  = rowTemp;
			break;
		}
	case '1':
		{
			colTemp = currentConsole->cursorX ;
			rowTemp = currentConsole->cursorY ;

			currentConsole->cursorY  = 0;
			currentConsole->cursorX  = 0;

			while (i++ < (rowTemp * currentConsole->windowWidth + colTemp))
				consolePrintChar(' ');

			currentConsole->cursorX  = colTemp;
			currentConsole->cursorY  = rowTemp;
			break;
		}
	case '2':
		{
			currentConsole->cursorY  = 0;
			currentConsole->cursorX  = 0;

			while(i++ < currentConsole->windowHeight * currentConsole->windowWidth)
				consolePrintChar(' ');

			currentConsole->cursorY  = 0;
			currentConsole->cursorX  = 0;
			break;
		}
	default: ;
	}
}
//---------------------------------------------------------------------------------
static void consoleClearLine(char mode) {
//---------------------------------------------------------------------------------

	int i = 0;
	int colTemp;

	switch (mode)
	{
	case '[':
	case '0':
		{
			colTemp = currentConsole->cursorX ;

			while(i++ < (currentConsole->windowWidth - colTemp)) {
				consolePrintChar(' ');
			}

			currentConsole->cursorX  = colTemp;

			break;
		}
	case '1':
		{
			colTemp = currentConsole->cursorX ;

			currentConsole->cursorX  = 0;

			while(i++ < ((currentConsole->windowWidth - colTemp)-2)) {
				consolePrintChar(' ');
			}

			currentConsole->cursorX  = colTemp;

			break;
		}
	case '2':
		{
			colTemp = currentConsole->cursorX ;

			currentConsole->cursorX  = 0;

			while(i++ < currentConsole->windowWidth) {
				consolePrintChar(' ');
			}

			currentConsole->cursorX  = colTemp;

			break;
		}
	default: ;
	}
}

__attribute__ ((format (scanf, 2, 3))) int fb_sscanf(const char *s, const char *fmt, ...)
{
	va_list args;
	va_start(args, fmt);


	int ret = 0;
	const char *const oldS = s;
	while(*fmt)
	{
		if(*fmt == '%')
		{
			BOOL number = FALSE;

			switch(*++fmt)
			{
				case 'd':
					*va_arg(args, int*) = atoi(s);
					number = TRUE;
					ret++;
					break;
				case 'c':
					*va_arg(args, char*) = *s++;
					ret++;
					break;
				case 'n':
					*va_arg(args, int*) = (int)(s - oldS);
					break;
				default: ;
			}
			if(number) while(*s >= '0' && *s <= '9') s++;
			fmt++;
		}
		else
		{
			if(*fmt != *s) break;
			fmt++;
			s++;
		}
	}

	va_end(args);

	return ret;
}

//---------------------------------------------------------------------------------
ssize_t con_write(const char *ptr, size_t len) {
//---------------------------------------------------------------------------------

	char chr;

	int i, count = 0;
	const char *tmp = ptr;

	if(!tmp || (int)len<=0) return -1;

	i = 0;

	while(i<(int)len) {

		chr = *(tmp++);
		i++; count++;

		if ( chr == 0x1b && *tmp == '[' ) {
			BOOL escaping = TRUE;
			const char *escapeseq = tmp++;
			int escapelen = 1;
			i++; count++;

			do {
				chr = *(tmp++);
				i++; count++; escapelen++;
				int parameter, assigned, consumed;

				// make sure parameters are positive values and delimited by semicolon
				if((chr >= '0' && chr <= '9') || chr == ';')
					continue;

				switch (chr) {
					//---------------------------------------
					// Cursor directional movement
					//---------------------------------------
					case 'A':
						consumed = 0;
						assigned = fb_sscanf(escapeseq,"[%dA%n", &parameter, &consumed);
						if (assigned==0) parameter = 1;
						if (consumed)
							currentConsole->cursorY  =  (currentConsole->cursorY  - parameter) < 0 ? 0 : currentConsole->cursorY  - parameter;
						escaping = FALSE;
						break;
					case 'B':
						consumed = 0;
						assigned = fb_sscanf(escapeseq,"[%dB%n", &parameter, &consumed);
						if (assigned==0) parameter = 1;
						if (consumed)
							currentConsole->cursorY  =  (currentConsole->cursorY  + parameter) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY  + parameter;
						escaping = FALSE;
						break;
					case 'C':
						consumed = 0;
						assigned = fb_sscanf(escapeseq,"[%dC%n", &parameter, &consumed);
						if (assigned==0) parameter = 1;
						if (consumed)
							currentConsole->cursorX  =  (currentConsole->cursorX  + parameter) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX  + parameter;
						escaping = FALSE;
						break;
					case 'D':
						consumed = 0;
						assigned = fb_sscanf(escapeseq,"[%dD%n", &parameter, &consumed);
						if (assigned==0) parameter = 1;
						if (consumed)
							currentConsole->cursorX  =  (currentConsole->cursorX  - parameter) < 0 ? 0 : currentConsole->cursorX  - parameter;
						escaping = FALSE;
						break;
					//---------------------------------------
					// Cursor position movement
					//---------------------------------------
					case 'H':
					case 'f':
					{
						int  x, y;
						char c;
						if(fb_sscanf(escapeseq,"[%d;%d%c", &y, &x, &c) == 3 && (c == 'f' || c == 'H')) {
							currentConsole->cursorX = x;
							currentConsole->cursorY = y;
							escaping = FALSE;
							break;
						}

						x = y = 1;
						if(fb_sscanf(escapeseq,"[%d;%c", &y, &c) == 2 && (c == 'f' || c == 'H')) {
							currentConsole->cursorX = x;
							currentConsole->cursorY = y;
							escaping = FALSE;
							break;
						}

						x = y = 1;
						if(fb_sscanf(escapeseq,"[;%d%c", &x, &c) == 2 && (c == 'f' || c == 'H')) {
							currentConsole->cursorX = x;
							currentConsole->cursorY = y;
							escaping = FALSE;
							break;
						}

						x = y = 1;
						if(fb_sscanf(escapeseq,"[;%c", &c) == 1 && (c == 'f' || c == 'H')) {
							currentConsole->cursorX = x;
							currentConsole->cursorY = y;
							escaping = FALSE;
							break;
						}

						// invalid format
						escaping = FALSE;
						break;
					}
					//---------------------------------------
					// Screen clear
					//---------------------------------------
					case 'J':
						if(escapelen <= 3)
							consoleCls(escapeseq[escapelen-2]);
						escaping = FALSE;
						break;
					//---------------------------------------
					// Line clear
					//---------------------------------------
					case 'K':
						if(escapelen <= 3)
							consoleClearLine(escapeseq[escapelen-2]);
						escaping = FALSE;
						break;
					//---------------------------------------
					// Save cursor position
					//---------------------------------------
					case 's':
						if(escapelen == 2) {
							currentConsole->prevCursorX  = currentConsole->cursorX ;
							currentConsole->prevCursorY  = currentConsole->cursorY ;
						}
						escaping = FALSE;
						break;
					//---------------------------------------
					// Load cursor position
					//---------------------------------------
					case 'u':
						if(escapelen == 2) {
							currentConsole->cursorX  = currentConsole->prevCursorX ;
							currentConsole->cursorY  = currentConsole->prevCursorY ;
						}
						escaping = FALSE;
						break;
					//---------------------------------------
					// Color scan codes
					//---------------------------------------
					case 'm':
						escapeseq++;
						escapelen--;

						do {
							parameter = 0;
							if (escapelen == 1) {
								consumed = 1;
							} else if (memchr(escapeseq,';',escapelen)) {
								fb_sscanf(escapeseq,"%d;%n", &parameter, &consumed);
							} else {
								fb_sscanf(escapeseq,"%dm%n", &parameter, &consumed);
							}

							escapeseq += consumed;
							escapelen -= consumed;

							switch(parameter) {
							case 0: // reset
								currentConsole->flags = 0;
								currentConsole->bg    = 0;
								currentConsole->fg    = 7;
								break;

							case 1: // bold
								currentConsole->flags &= ~CONSOLE_COLOR_FAINT;
								currentConsole->flags |= CONSOLE_COLOR_BOLD;
								break;

							case 2: // faint
								currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
								currentConsole->flags |= CONSOLE_COLOR_FAINT;
								break;

							case 3: // italic
								currentConsole->flags |= CONSOLE_ITALIC;
								break;

							case 4: // underline
								currentConsole->flags |= CONSOLE_UNDERLINE;
								break;

							case 5: // blink slow
								currentConsole->flags &= ~CONSOLE_BLINK_FAST;
								currentConsole->flags |= CONSOLE_BLINK_SLOW;
								break;

							case 6: // blink fast
								currentConsole->flags &= ~CONSOLE_BLINK_SLOW;
								currentConsole->flags |= CONSOLE_BLINK_FAST;
								break;

							case 7: // reverse video
								currentConsole->flags |= CONSOLE_COLOR_REVERSE;
								break;

							case 8: // conceal
								currentConsole->flags |= CONSOLE_CONCEAL;
								break;

							case 9: // crossed-out
								currentConsole->flags |= CONSOLE_CROSSED_OUT;
								break;

							case 21: // bold off
								currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
								break;

							case 22: // normal color
								currentConsole->flags &= ~CONSOLE_COLOR_BOLD;
								currentConsole->flags &= ~CONSOLE_COLOR_FAINT;
								break;

							case 23: // italic off
								currentConsole->flags &= ~CONSOLE_ITALIC;
								break;

							case 24: // underline off
								currentConsole->flags &= ~CONSOLE_UNDERLINE;
								break;

							case 25: // blink off
								currentConsole->flags &= ~CONSOLE_BLINK_SLOW;
								currentConsole->flags &= ~CONSOLE_BLINK_FAST;
								break;

							case 27: // reverse off
								currentConsole->flags &= ~CONSOLE_COLOR_REVERSE;
								break;

							case 29: // crossed-out off
								currentConsole->flags &= ~CONSOLE_CROSSED_OUT;
								break;

							case 30 ... 37: // writing color
								currentConsole->fg = parameter - 30;
								break;

							case 39: // reset foreground color
								currentConsole->fg = 7;
								break;

							case 40 ... 47: // screen color
								currentConsole->bg = parameter - 40;
								break;

							case 49: // reset background color
								currentConsole->fg = 0;
								break;
							default: ;
							}
						} while (escapelen > 0);

						escaping = FALSE;
						break;

					default:
						// some sort of unsupported escape; just gloss over it
						escaping = FALSE;
						break;
				}
			} while (escaping);
			continue;
		}

		consolePrintChar(chr);
	}

	return count;
}

//---------------------------------------------------------------------------------
PrintConsole* consoleInit(PrintConsole* console, BOOL clear, VOID *fbaddr, int width, int height, int pixelBytes) {
//---------------------------------------------------------------------------------

    u32 size = 0;

	if(console) {
		currentConsole = console;
	} else {
		console = currentConsole;
	}

	*currentConsole = defaultConsole;

    size = width * height * pixelBytes;

    console->ScreenWidth   = width;
    console->ScreenHeight  = height;
    console->ScreenPixelBytes  = pixelBytes;

    console->consoleWidth  = width / FONT_WIDTH;
    console->consoleHeight = height / FONT_HEIGHT;
    console->windowWidth   = width / FONT_WIDTH;
    console->windowHeight  = height / FONT_HEIGHT;

	console->consoleInitialised = 1;

    console->frameBuffer = (char*)fbaddr;

	if(clear) consoleCls('2');

    console->frameBufferCache = (char*)malloc(size);
    memset(console->frameBufferCache, 0, size);

	return currentConsole;

}

//---------------------------------------------------------------------------------
PrintConsole* consoleTopInit(VOID *fbaddr, int width, int height, int pixelBytes) {
//---------------------------------------------------------------------------------

    return consoleInit(&topCon, TRUE, fbaddr, width, height, pixelBytes);
}

//---------------------------------------------------------------------------------
PrintConsole* consoleBotInit(VOID *fbaddr, int width, int height, int pixelBytes) {
//---------------------------------------------------------------------------------

    return consoleInit(&botCon, TRUE, fbaddr, width, height, pixelBytes);
}

//---------------------------------------------------------------------------------
VOID consoleFbDevInit(BOOL botInit) {
//---------------------------------------------------------------------------------
    LW_GM_VARINFO varInfo;
    size_t screensize = 0;
    void *fb_addr = NULL;
    SIO_CHAN *psio;
    int fd, width, height, pixelBytes;

    fd = open("/dev/fb0", O_RDWR);
    ioctl(fd, LW_GM_GET_VARINFO, &varInfo);

    screensize = varInfo.GMVI_ulXRes * varInfo.GMVI_ulYRes * varInfo.GMVI_ulBytesPerPixel;
    fb_addr = mmap(LW_NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    memset(fb_addr, 0, screensize);

    if (botInit) {
        width = varInfo.GMVI_ulXRes;
        height = varInfo.GMVI_ulYRes / 2;
        pixelBytes = varInfo.GMVI_ulBytesPerPixel;
    } else {
        width = varInfo.GMVI_ulXRes;
        height = varInfo.GMVI_ulYRes;
        pixelBytes = varInfo.GMVI_ulBytesPerPixel;
    }

    consoleTopInit(fb_addr, width, height, pixelBytes);

    if (botInit) {
        consoleBotInit(fb_addr + screensize / 2, width, height, pixelBytes);
    }

    psio = iFbConsoleSioChanCreate();
    if (psio) {
        ttyDevCreate("/dev/console", psio, 512, 512);
    }
}

//---------------------------------------------------------------------------------
PrintConsole *consoleSelect(PrintConsole* console){
//---------------------------------------------------------------------------------
	PrintConsole *tmp = currentConsole;
	currentConsole = console;
	return tmp;
}

//---------------------------------------------------------------------------------
VOID consoleTopPutMsg(CPCHAR  pcMsg){
//---------------------------------------------------------------------------------
    consoleSelect(&topCon);
    ee_printf(pcMsg);
}

//---------------------------------------------------------------------------------
VOID consoleBotPutMsg(CPCHAR  pcMsg){
//---------------------------------------------------------------------------------
    consoleSelect(&botCon);
    ee_printf(pcMsg);
}

//---------------------------------------------------------------------------------
PrintConsole *consoleGet(void){
//---------------------------------------------------------------------------------
	return currentConsole;
}

//---------------------------------------------------------------------------------
u32 consoleGetFgColor(void){
//---------------------------------------------------------------------------------
	return colorTable[currentConsole->fg];
}

//---------------------------------------------------------------------------------
void consoleSetFont(PrintConsole* console, ConsoleFont* font){
//---------------------------------------------------------------------------------

	if(!console) console = currentConsole;

	console->font = *font;

}

//---------------------------------------------------------------------------------
static void newRow() {
//---------------------------------------------------------------------------------


	currentConsole->cursorY ++;


	if(currentConsole->cursorY  >= currentConsole->windowHeight)  {
		currentConsole->cursorY --;

		void *src;
		void *dst;

	    if (currentConsole->frameBufferCache) {
	        if (needCopy) {
	            needCopy = 0;
	        }
	        dst = currentConsole->frameBufferCache;
	    } else {
	        dst = currentConsole->frameBuffer;
	    }

        if (currentConsole->ScreenPixelBytes == 4) {
            src = (u32 *)dst + (currentConsole->ScreenWidth * FONT_HEIGHT);
        } else if (currentConsole->ScreenPixelBytes == 2) {
            src = (u16 *)dst + (currentConsole->ScreenWidth * FONT_HEIGHT);
        } else {
            src = (u32 *)dst + (currentConsole->ScreenWidth * FONT_HEIGHT);
        }

		u32 size = (currentConsole->ScreenWidth * currentConsole->ScreenPixelBytes) * (FONT_HEIGHT * (currentConsole->windowHeight - 1));

		memcpy(dst, src, size);

		consoleClearLine('2');

		if (currentConsole->frameBufferCache) {
		    memcpy(currentConsole->frameBuffer,
		           currentConsole->frameBufferCache,
		           currentConsole->ScreenWidth * currentConsole->ScreenHeight * currentConsole->ScreenPixelBytes);
		}
	}
}
//---------------------------------------------------------------------------------
void consoleDrawChar(int c) {
//---------------------------------------------------------------------------------
	c -= currentConsole->font.asciiOffset;
	if ( c < 0 || c > currentConsole->font.numChars ) return;

	const u8 *fontdata = currentConsole->font.gfx + (FONT_HEIGHT * c);

#if 1
	int writingColor = currentConsole->fg;
	int screenColor = currentConsole->bg;

	u32 bg;
	u32 fg;
#else
    u32 bg = 0x0;
    u32 fg = 0xffffffff;
#endif

    u8 b;
	u8 mask;
	int i,j;

	// convert cursor_xy to pixel_xy
	int x = currentConsole->cursorX * FONT_WIDTH;
	int y = currentConsole->cursorY * FONT_HEIGHT;

	u32 *screen = NULL;
	void *src, *dst = NULL;

    u16 *screen16 = NULL;

    if (currentConsole->ScreenPixelBytes == 4) {
        bg = colorTable[screenColor];
        fg = colorTable[writingColor];
    } else if (currentConsole->ScreenPixelBytes == 2) {
        bg = colorTable16[screenColor];
        fg = colorTable16[writingColor];
    } else {
        bg = colorTable[screenColor];
        fg = colorTable[writingColor];
    }

	if (currentConsole->frameBufferCache) {
	    // copy data from fb to fbcache
	    if (needCopy) {
	        needCopy = 0;
	        memcpy(currentConsole->frameBufferCache,
	               currentConsole->frameBuffer,
	               (y * currentConsole->ScreenWidth + x) * currentConsole->ScreenPixelBytes);
	    }

	    if (currentConsole->ScreenPixelBytes == 4) {
	        src = &((u32 *)currentConsole->frameBufferCache)[(y * currentConsole->ScreenWidth) + x];
	        dst = &((u32 *)currentConsole->frameBuffer)[(y * currentConsole->ScreenWidth) + x];
	    } else if (currentConsole->ScreenPixelBytes == 2) {
            src = &((u16 *)currentConsole->frameBufferCache)[(y * currentConsole->ScreenWidth) + x];
            dst = &((u16 *)currentConsole->frameBuffer)[(y * currentConsole->ScreenWidth) + x];
	    } else {
            src = &((u32 *)currentConsole->frameBufferCache)[(y * currentConsole->ScreenWidth) + x];
            dst = &((u32 *)currentConsole->frameBuffer)[(y * currentConsole->ScreenWidth) + x];
	    }
	} else {
        if (currentConsole->ScreenPixelBytes == 4) {
            src = &((u32 *)currentConsole->frameBuffer)[(y * currentConsole->ScreenWidth) + x];
        } else if (currentConsole->ScreenPixelBytes == 2) {
            src = &((u16 *)currentConsole->frameBuffer)[(y * currentConsole->ScreenWidth) + x];
        } else {
            src = &((u32 *)currentConsole->frameBuffer)[(y * currentConsole->ScreenWidth) + x];
        }
	}

    screen = src;
    screen16 = src;

	for (i = 0; i < FONT_HEIGHT; i++) {
	    mask = 0x80;
	    b = *(fontdata++);

	    for (j = 0; j < FONT_WIDTH; j++) {
	        if (b & mask) {
	            if (currentConsole->ScreenPixelBytes == 4) {
	                *(screen++) = fg;
	            } else if (currentConsole->ScreenPixelBytes == 2) {
	                *(screen16++) = fg;
	            } else {
	                *(screen++) = fg;
	            }
	        } else {
                if (currentConsole->ScreenPixelBytes == 4) {
                    *(screen++) = bg;
                } else if (currentConsole->ScreenPixelBytes == 2) {
                    *(screen16++) = bg;
                } else {
                    *(screen++) = bg;
                }
	        }

            mask >>= 1;
	    }

        if (currentConsole->ScreenPixelBytes == 4) {
            screen += (currentConsole->ScreenWidth - FONT_WIDTH);
        } else if (currentConsole->ScreenPixelBytes == 2) {
            screen16 += (currentConsole->ScreenWidth - FONT_WIDTH);
        } else {
            screen += (currentConsole->ScreenWidth - FONT_WIDTH);
        }
	}

	//flush data
    if (currentConsole->frameBufferCache) {
        for (i = 0; i < FONT_HEIGHT; i++) {
            memcpy(dst, src, FONT_WIDTH * currentConsole->ScreenPixelBytes);

            if (currentConsole->ScreenPixelBytes == 4) {
                src = (u32 *)src + currentConsole->ScreenWidth;
                dst = (u32 *)dst + currentConsole->ScreenWidth;
            } else if (currentConsole->ScreenPixelBytes == 2) {
                src = (u16 *)src + currentConsole->ScreenWidth;
                dst = (u16 *)dst + currentConsole->ScreenWidth;
            } else {
                src = (u32 *)src + currentConsole->ScreenWidth;
                dst = (u32 *)dst + currentConsole->ScreenWidth;
            }
        }
    }
}

//---------------------------------------------------------------------------------
void consolePrintChar(int c) {
//---------------------------------------------------------------------------------
	if (c==0) return;

	if(currentConsole->PrintChar)
		if(currentConsole->PrintChar(currentConsole, c))
			return;

	if(currentConsole->cursorX  >= currentConsole->windowWidth) {
		currentConsole->cursorX  = 0;

		newRow();
	}

	switch(c) {
		/*
		The only special characters we will handle are tab (\t), carriage return (\r), line feed (\n)
		and backspace (\b).
		Carriage return & line feed will function the same: go to next line and put cursor at the beginning.
		For everything else, use VT sequences.

		Reason: VT sequences are more specific to the task of cursor placement.
		The special escape sequences \b \f & \v are archaic and non-portable.
		*/
		case 8:
			currentConsole->cursorX--;

			if(currentConsole->cursorX < 0) {
				if(currentConsole->cursorY > 0) {
					currentConsole->cursorX = currentConsole->windowX - 1;
					currentConsole->cursorY--;
				} else {
					currentConsole->cursorX = 0;
				}
			}

			consoleDrawChar(' ');
			break;

		case 9:
			currentConsole->cursorX  += currentConsole->tabSize - ((currentConsole->cursorX)%(currentConsole->tabSize));
			break;
		case 10:
			newRow();
			// falls through
		case 13:
			currentConsole->cursorX  = 0;
			break;
		default:
			consoleDrawChar(c);
			++currentConsole->cursorX ;
			break;
	}
}

//---------------------------------------------------------------------------------
void consoleClear(void) {
//---------------------------------------------------------------------------------
	ee_printf("\x1b[2J");
}

//---------------------------------------------------------------------------------
void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height){
//---------------------------------------------------------------------------------

	if(!console) console = currentConsole;

	console->windowWidth = width;
	console->windowHeight = height;
	console->windowX = x;
	console->windowY = y;

	console->cursorX = 0;
	console->cursorY = 0;

}

//not use
#if 0
void drawConsoleWindow(PrintConsole* console, int thickness, u8 colorIndex) {

	if(colorIndex >= 16) return;

	if(!console) console = currentConsole;
	
	int startx = console->windowX * 8 - thickness;
	int endx = (console->windowX + console->windowWidth) * 8 + thickness;
	
	int starty = (console->windowY - 1) * 8 - thickness;
	int endy = console->windowHeight * 8 + thickness;

	u16 color = colorTable[colorIndex];
	
	// upper line
	for(int y = starty; y < starty + thickness; y++)
		for(int x = startx; x < endx; x++)
		{
			u16 *screen = &currentConsole->frameBuffer[(x * 240) + (239 - (y + 7))];
			*screen = color;
		}
	
	// lower line
	for(int y = endy; y > endy - thickness; y--)
		for(int x = startx; x < endx; x++)
		{
			u16 *screen = &currentConsole->frameBuffer[(x * 240) + (239 - (y + 7))];
			*screen = color;
		}
		
	// left and right
	for(int y = starty; y < endy; y++)
	{
		for(int i = 0; i < thickness; i++)
		{
			u16 *screen = &currentConsole->frameBuffer[((startx + i) * 240) + (239 - (y + 7))];
			*screen = color;
			screen = &currentConsole->frameBuffer[((endx - thickness + i) * 20) + (239 - (y + 7))];
			*screen = color;
		}
	}
}
#endif

void consoleSetCursor(PrintConsole* console, int x, int y) {
	console->cursorX = x;
	console->cursorY = y;
}
